/*---------------------------------------------------------------------
 * File name: halport.cpp
 *
 * Copyright (c) <2020-2022>, <ChenLong>
 *
 * All rights reserved.
 *
 * Author: ChenLong
 * Email: worldlong@foxmail.com
 *--------------------------------------------------------------------*/
#include "stm32g4xx.h"
#include "hal_gpio.h"
#include "hal_config.h"
#include "hal_def.h"
#if defined(USING_RT_THREAD)
#include "rtthread.h" 
#endif
/*
*/
static void (*irq_hook[16])(void *param) = {nullptr};
static void *irq_hook_param[16]={nullptr};
const static struct {
  IRQn_Type irqn;
  uint8_t priority;
}nvic_table[]={
  {EXTI0_IRQn, //EXTI0
   NVIC_IRQ_EXTI0_PRIORITY},
  {EXTI1_IRQn, //EXTI1
   NVIC_IRQ_EXTI1_PRIORITY},
  {EXTI2_IRQn, //EXTI2
   NVIC_IRQ_EXTI2_PRIORITY},
  {EXTI3_IRQn, //EXTI3
   NVIC_IRQ_EXTI3_PRIORITY},
  {EXTI4_IRQn, //EXTI4
   NVIC_IRQ_EXTI4_PRIORITY},
  {EXTI9_5_IRQn, //EXTI5~//EXTI9
   NVIC_IRQ_EXTI9_5_PRIORITY},
  {EXTI15_10_IRQn, //EXTI10 ~ EXTI15
   NVIC_IRQ_EXTI15_10_PRIORITY}
};
/*
*/
bool HAL_GPIO::init(uint64_t cfg0)
{ 
  cfg = cfg0;
  uint32_t tmp = HGPIO_CFG_EXTRACT(cfg, PORT);
  
  if(tmp == 0) { //No port
    return false;
  }
#if GPIOA_ENABLE
  else if(tmp == HGPIO_PORT(A)) {
    port = (uint32_t)GPIOA;
  }
#endif
#if GPIOB_ENABLE
  else if(tmp == HGPIO_PORT(B)) {
    port = (uint32_t)GPIOB;
  } 
#endif
#if GPIOC_ENABLE
  else if(tmp == HGPIO_PORT(C)) {
    port = (uint32_t)GPIOC;
  }
#endif
#if GPIOD_ENABLE
  else if(tmp == HGPIO_PORT(D)) {
    port = (uint32_t)GPIOD;
  }
#endif
#if GPIOE_ENABLE
  else if(tmp == HGPIO_PORT(E)) {
    port = (uint32_t)GPIOE;
  }
#endif
#if GPIOF_ENABLE
  else if(tmp == HGPIO_PORT(F)) {
    port = (uint32_t)GPIOF;
  } 
#endif
#if GPIOG_ENABLE
  else if(tmp == HGPIO_PORT(G)) {
    port = (uint32_t)GPIOG;
  } 
#endif
#if GPIOH_ENABLE
  else if(tmp == HGPIO_PORT(H)) {
    port = (uint32_t)GPIOH;
  } 
#endif
  else if(port == 0){
    return false;
  }
  
  return config(cfg);
}
/*
*/
bool HAL_GPIO::config(uint64_t cfg0)
{
  cfg &= ~HGPIO_CFG_MASK;
  cfg |= cfg0 & HGPIO_CFG_MASK;
  
  GPIO_InitTypeDef  GPIO_InitStructure;
 
  //pin
  pin = HGPIO_CFG_EXTRACT(cfg, PIN);
  GPIO_InitStructure.Pin = pin;
  if(pin == 0) {
    return false;
  }
  
  //pin number, just used for only one pin
  for(int i=0; i<16; i++) {
    if((pin >> i) & 0x01) {
      _index = i;
      break;
    }
  }
  
  //type
  uint32_t type = HGPIO_CFG_EXTRACT(cfg, TYPE);
  if(type == HGPIO_TYPE(PU)) {
    GPIO_InitStructure.Pull = GPIO_PULLUP;
  } else if(type == HGPIO_TYPE(PD)) {
    GPIO_InitStructure.Pull = GPIO_PULLDOWN;
  } else {
    GPIO_InitStructure.Pull = GPIO_NOPULL;
  }
  
  //speed
  uint32_t speed = HGPIO_CFG_EXTRACT(cfg, SPEED);
  if(speed == HGPIO_SPEED(SLOW)) {
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
  } else if(speed == HGPIO_SPEED(MEDIUM)) {
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;
  } else if(speed == HGPIO_SPEED(HIGH)) {
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
  } else if(speed == HGPIO_SPEED(FAST)) {
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  } else {
    return false;
  }
    
  //mode
  uint32_t mode = HGPIO_CFG_EXTRACT(cfg, MODE);
  uint32_t exti = HGPIO_CFG_EXTRACT(cfg, EXTI);
  
  if(mode == HGPIO_MODE(OUT)) {
    if(type == HGPIO_TYPE(PP)) {
      GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    } else {
      GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
    }
  } else if(mode == HGPIO_MODE(IN)) {
    if(exti == HGPIO_EXTI(INTRT)) {
      GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING;
    } else if(exti == HGPIO_EXTI(INTFT)) {
      GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
    } else if(exti == HGPIO_EXTI(INTRFT)) {
      GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING_FALLING;
    } else if(exti == HGPIO_EXTI(EVTRT)) {
      GPIO_InitStructure.Mode = GPIO_MODE_EVT_RISING;
    } else if(exti == HGPIO_EXTI(EVTFT)) {
      GPIO_InitStructure.Mode = GPIO_MODE_EVT_FALLING;
    } else if(exti == HGPIO_EXTI(EVTRFT)) {
      GPIO_InitStructure.Mode = GPIO_MODE_EVT_RISING_FALLING;
    } else {
      GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
    } 
  } else if(mode == HGPIO_MODE(AN)) {
    GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
  } else { //AF0 ~ AF15
    if(type == HGPIO_TYPE(PP)) {
      GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
    } else {
      GPIO_InitStructure.Mode = GPIO_MODE_AF_OD;
    }
    if(mode >= HGPIO_MODE(AF0)) {
      GPIO_InitStructure.Alternate = mode - HGPIO_MODE(AF0);
    }
  }
  
  HAL_GPIO_Init((GPIO_TypeDef*)port, &GPIO_InitStructure);
  //status
  if(mode == HGPIO_MODE(OUT)) {
    uint32_t status = HGPIO_CFG_EXTRACT(cfg, STATUS);
    if(status == HGPIO_STATUS(SET)) {
      ((GPIO_TypeDef*)port)->ODR |= 0xffff & pin;
    } else {
      ((GPIO_TypeDef*)port)->ODR &= ~(0xffff & pin);
    }
  }
  
  //NVIC config
  int index = _index;
  if(index < 5) {
  } else if(index < 10) {
    index = 5;
  } else {
    index= 6;
  }
  if(exti != HGPIO_EXTI(NONE)) {
    HAL_NVIC_SetPriority(nvic_table[index].irqn, nvic_table[index].priority, 0);
    HAL_NVIC_EnableIRQ(nvic_table[index].irqn);
  }
   
  return true;
}
/*
*/
uint16_t HAL_GPIO::read()
{
  if(port) {
    GPIO_TypeDef *GPIOx = (GPIO_TypeDef*)port;
    return (GPIOx->IDR & pin);
  }
  return 0;
}
/*
*/
void HAL_GPIO::write(uint16_t val)
{
  if(port) {
    GPIO_TypeDef *GPIOx = (GPIO_TypeDef*)port;
    GPIOx->ODR = (GPIOx->ODR & ~pin) | (val & pin);
  }
}
/*
*/
void HAL_GPIO::toggle()
{
  if(port) {
    GPIO_TypeDef *GPIOx = (GPIO_TypeDef*)port;
    GPIOx->ODR = GPIOx->ODR ^ pin;
  }
}

/*
*/
void HAL_GPIO::attach_irq(void(*fun)(void *param), void *param)
{
  irq_hook[_index] = fun;
  irq_hook_param[_index] = param;
}

/*************************************************
              IRQHandler
**************************************************/
#ifdef __cplusplus
 extern "C" {
#endif
/*
*/
void EXTI0_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(REG_BIT(EXTI->PR1, 0) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 0);   //write 1 to clear
    if(irq_hook[0] != nullptr)
    {
      irq_hook[0](irq_hook_param[0]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI1_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif  
  if(REG_BIT(EXTI->PR1, 1) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 1);   //write 1 to clear
    if(irq_hook[1] != nullptr)
    {
      irq_hook[1](irq_hook_param[1]);
    }
  }   
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI2_TS_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(REG_BIT(EXTI->PR1, 2) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 2);   //write 1 to clear
    if(irq_hook[2] != nullptr)
    {
      irq_hook[2](irq_hook_param[2]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI3_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(REG_BIT(EXTI->PR1, 3) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 3);   //write 1 to clear
    if(irq_hook[3] != nullptr)
    {
      irq_hook[3](irq_hook_param[3]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI4_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(REG_BIT(EXTI->PR1, 4) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 4);   //write 1 to clear
    if(irq_hook[4] != nullptr)
    {
      irq_hook[4](irq_hook_param[4]);
    }
  } 
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI9_5_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  //Line5
  if(REG_BIT(EXTI->PR1, 5) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 5);   //write 1 to clear 
    if(irq_hook[5] != nullptr)
    {
      irq_hook[5](irq_hook_param[5]);
    }
  }
  
  //Line6
  if(REG_BIT(EXTI->PR1, 6) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 6);   //write 1 to clear
    if(irq_hook[6] != nullptr)
    {
      irq_hook[6](irq_hook_param[6]);
    }
  }  
  
  //Line7
  if(REG_BIT(EXTI->PR1, 7) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 7);   //write 1 to clear 
    if(irq_hook[7] != nullptr)
    {
      irq_hook[7](irq_hook_param[7]);
    }
  }  
  
  //Line8
  if(REG_BIT(EXTI->PR1, 8) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 8);   //write 1 to clear 
    if(irq_hook[8] != nullptr)
    {
      irq_hook[8](irq_hook_param[8]);
    }
  }  
  
  //Line9
  if(REG_BIT(EXTI->PR1, 9) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 9);   //write 1 to clear
    if(irq_hook[9] != nullptr)
    {
      irq_hook[9](irq_hook_param[9]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI15_10_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  //Line5
  if(REG_BIT(EXTI->PR1, 10) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 10);   //write 1 to clear
    if(irq_hook[10] != nullptr)
    {
      irq_hook[10](irq_hook_param[10]);
    }
  }
  
  //Line11
  if(REG_BIT(EXTI->PR1, 11) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 11);   //write 1 to clear
    if(irq_hook[11] != nullptr)
    {
      irq_hook[11](irq_hook_param[11]);
    }
  }  

  //Line12
  if(REG_BIT(EXTI->PR1, 12) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 12);   //write 1 to clear
    if(irq_hook[12] != nullptr)
    {
      irq_hook[12](irq_hook_param[12]);
    }
  }  
  
  //Line13
  if(REG_BIT(EXTI->PR1, 13) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 13);   //write 1 to clear
    if(irq_hook[13] != nullptr)
    {
      irq_hook[13](irq_hook_param[13]);
    }
  }  

  //Line14
  if(REG_BIT(EXTI->PR1, 14) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 14);   //write 1 to clear 
    if(irq_hook[14] != nullptr)
    {
      irq_hook[14](irq_hook_param[14]);
    }
  }  
  
  //Line15
  if(REG_BIT(EXTI->PR1, 15) != RESET)   
  { 
    REG_BIT_SET(EXTI->PR1, 15);   //write 1 to clear
    if(irq_hook[15] != nullptr)
    {
      irq_hook[15](irq_hook_param[15]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
#ifdef __cplusplus
}
#endif




